﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
using System.ComponentModel.Design;
using Microsoft.Win32;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.VisualStudio.TeamFoundation.WorkItemTracking;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System.Collections.Generic;
using Microsoft.VisualStudio.TeamFoundation;
using Microsoft.TeamFoundation.Common;
using System.Windows.Forms;
using System.Collections;
using System.Text;
using Microsoft.TeamFoundation;
using System.Linq;
using MSWord = Microsoft.Office.Interop.Word;
using System.IO;
using System.Drawing;

namespace TFSExt.ExportWordDoc
{
    /// <summary>
    /// This is the class that implements the package exposed by this assembly.
    ///
    /// The minimum requirement for a class to be considered a valid package for Visual Studio
    /// is to implement the IVsPackage interface and register itself with the shell.
    /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
    /// to do it: it derives from the Package class that provides the implementation of the 
    /// IVsPackage interface and uses the registration attributes defined in the framework to 
    /// register itself and its components with the shell.
    /// </summary>
    // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
    // a package.
    [PackageRegistration(UseManagedResourcesOnly = true)]
    // This attribute is used to register the informations needed to show the this package
    // in the Help/About dialog of Visual Studio.
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
    // This attribute is needed to let the shell know that this package exposes some menus.
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [ProvideLoadKey("Standard", "1.0", "TFS Productivity Tools - Export to Word", "Mark Babayev (Experis)", 114)]
    [Guid(GuidList.guidExportWordDocPkgString)]
    public sealed class ExportWordDocPackage : Package
    {
        WorkItemStore wistore;
        IVsTeamExplorer vsTeamExp;
        string AppTitle;
        IVsThreadedWaitDialog2 dlg;
        
        /// <summary>
        /// Default constructor of the package.
        /// Inside this method you can place any initialization code that does not require 
        /// any Visual Studio service because at this point the package object is created but 
        /// not sited yet inside Visual Studio environment. The place to do all the other 
        /// initialization is the Initialize method.
        /// </summary>
        public ExportWordDocPackage()
        {
            Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
        }

        private class Tag: IEquatable<Tag>
        {
            public int tWorkItemID, tlevel, tParentID;
            public string tLinkTypeID;
            public string tPrefix;
        
            public bool  Equals(Tag other)
            {
                return tWorkItemID == other.tWorkItemID && (other.tlevel < 0 ? true : tlevel == other.tlevel);
            }
        }

        /////////////////////////////////////////////////////////////////////////////
        // Overriden Package Implementation
        #region Package Members

        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initilaization code that rely on services provided by VisualStudio.
        /// </summary>
        protected override void Initialize()
        {
            Trace.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));
            base.Initialize();

            // Add our command handlers for menu (commands must exist in the .vsct file)
            OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if ( null != mcs )
            {
                // Create the command for the menu item.
                CommandID menuCommandID = new CommandID(GuidList.guidExportWordDocCmdSet, (int)PkgCmdIDList.cmdidMyCommand);
                MenuCommand menuItem = new MenuCommand(TeamExpQueryCallback, menuCommandID);
                mcs.AddCommand( menuItem );
            }
        }
        #endregion

        private void InitializeVariables()
        {
            var dte = (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE));
            //IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));

            var TFSExt = dte.GetObject("Microsoft.VisualStudio.TeamFoundation.TeamFoundationServerExt") as TeamFoundationServerExt;

            var tfscoll = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(TFSExt.ActiveProjectContext.DomainUri));
            tfscoll.Connect(Microsoft.TeamFoundation.Framework.Common.ConnectOptions.None);
            tfscoll.EnsureAuthenticated();

            var vcsrv = (VersionControlServer)tfscoll.GetService(typeof(VersionControlServer));
            wistore = tfscoll.GetService<WorkItemStore>();

            AppTitle = Resources.ResourceManager.GetString("AppTitle");

            vsTeamExp = (IVsTeamExplorer)GetService(typeof(IVsTeamExplorer));
        }

        private IVsThreadedWaitDialog2 CreateThreadedWaitDialog(string caption, string message, string progress, string statustext, int total)
        {
            IVsThreadedWaitDialogFactory dialogFactory = GetService(typeof(SVsThreadedWaitDialogFactory)) as IVsThreadedWaitDialogFactory;
            IVsThreadedWaitDialog2 dlg = null;
            ErrorHandler.ThrowOnFailure(dialogFactory.CreateInstance(out dlg));

            ErrorHandler.ThrowOnFailure(
                dlg.StartWaitDialogWithPercentageProgress(caption, message, progress, null, statustext, true, 0, total, 0));

            return dlg;
        }

        /// <summary>
        /// Menus the item callback.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void TeamExpQueryCallback(object sender, EventArgs e)
        {
            InitializeVariables();
            var origCursor = Cursor.Current;
            Cursor.Current = Cursors.WaitCursor;
            
            IntPtr hier;
            uint itemid;
            IVsMultiItemSelect dummy;
            string canonicalName;
            bool bcanceled;
            int icanceled;
            string OperationCaption = "Exporting Work Item query to Microsoft Word document";

            dlg = CreateThreadedWaitDialog(AppTitle, OperationCaption, "Parsing query...", "status", 100);
            dlg.UpdateProgress(OperationCaption, "Parsing query...", "status", 1, 100, false, out bcanceled);

            vsTeamExp.TeamExplorerWindow.GetCurrentSelection(out hier, out itemid, out dummy);
            IVsHierarchy hierarchy = (IVsHierarchy)Marshal.GetObjectForIUnknown(hier);
            Marshal.Release(hier);

            //hierarchy.GetProperty(itemid, (int)__VSHPROPID.VSHPROPID_ExtSelectedItem, out res);
            //MessageBox.Show(res.GetType().ToString());

            hierarchy.GetCanonicalName(itemid, out canonicalName);
            string[] tokens = canonicalName.Split('/');
            string ProjectName = tokens[1];
            var proj = wistore.Projects[ProjectName];

            QueryItem qItem = proj.QueryHierarchy;            
            
            int currentTokenIndex = 2;            
            while (currentTokenIndex < tokens.Length)            
            {                
                qItem = (qItem as QueryFolder)[tokens[currentTokenIndex]];                
                currentTokenIndex++;            
            }

            //string[] qheader;
            string[][] qdata;
            bcanceled = ExecuteQueryLimitedFields(wistore, qItem as QueryDefinition, ProjectName, out qdata);
            dlg.UpdateProgress(OperationCaption, "Creating new Word document...", "status", 1, 100, false, out bcanceled);

            try
            {
                //if (!bcanceled)
                CreateWordDocParagraph((qItem as QueryDefinition).Name, qdata);
            }
            catch (Exception ex)
            {
                OutputCommandString(ex.Message, AppTitle);
            }
            finally
            {
                Cursor.Current = origCursor;
            }

            dlg.EndWaitDialog(out icanceled);
        }

        private void CreateWordDocTable(string[] qheader, string[][] qdata)
        {
            var application = new MSWord.Application();
            var document = new MSWord.Document();
            //object missing = System.Reflection.Missing.Value;

            application.Visible = true;
            application.DisplayAlerts = MSWord.WdAlertLevel.wdAlertsNone;
            document = application.Documents.Add();
            document.PageSetup.Orientation = MSWord.WdOrientation.wdOrientLandscape;
            document.PageSetup.LeftMargin = 20;
            document.PageSetup.RightMargin = 20;
            document.PageSetup.TopMargin = 20;
            document.PageSetup.BottomMargin = 20;
            document.PageSetup.PageWidth = qheader.Length * 100 + 200 + 40;

            MSWord.Table qtable = document.Tables.Add(document.Range(), qdata.Length+1, qheader.Length);
            MSWord.Style styl = CreateTableStyle(ref document);
            qtable.Range.set_Style(styl);
            qtable.Borders[MSWord.WdBorderType.wdBorderBottom].LineStyle = MSWord.WdLineStyle.wdLineStyleDouble;
            qtable.Columns.Width = 100;
            qtable.Columns[1].Width = 300;
            
            for (int i = 0; i < qheader.Length; i++)
            {
                qtable.Cell(1, i+1).Range.Text = qheader[i];
            }

            for (int j = 0; j < qdata.Length; j++)
            {
                //int level = Int16.Parse(qdata[j][0]);
                //qtable.Cell(j + 2, 1).Range.

                for (int i = 0; i < qheader.Length; i++)
                {
                    qtable.Cell(j+2, i+1).Range.Text = qdata[j][i];
                }
            }

            Marshal.ReleaseComObject(document);
            Marshal.ReleaseComObject(application);
        }

        private bool CreateWordDocParagraph(string qname, string[][] qdata)
        {
            //InitializeVariables();
            System.Net.WebClient webclient = new System.Net.WebClient();
            webclient.Credentials = System.Net.CredentialCache.DefaultCredentials;
            
            var application = new MSWord.Application();
            var document = new MSWord.Document();
            object missing = System.Reflection.Missing.Value;
            int pagewidth = 800;
            bool bcanceled;
            int progress = 1;
            StringBuilder strbld = new StringBuilder();

            MSWord.WdBuiltinStyle[] hstyles = new MSWord.WdBuiltinStyle[8];
            hstyles[0] = MSWord.WdBuiltinStyle.wdStyleHeading1;
            hstyles[1] = MSWord.WdBuiltinStyle.wdStyleHeading2;
            hstyles[2] = MSWord.WdBuiltinStyle.wdStyleHeading3;
            hstyles[3] = MSWord.WdBuiltinStyle.wdStyleHeading4;
            hstyles[4] = MSWord.WdBuiltinStyle.wdStyleHeading5;
            hstyles[5] = MSWord.WdBuiltinStyle.wdStyleHeading6;
            hstyles[6] = MSWord.WdBuiltinStyle.wdStyleHeading7;
            hstyles[7] = MSWord.WdBuiltinStyle.wdStyleHeading8;

            application.Visible = true;
            application.WindowState = MSWord.WdWindowState.wdWindowStateMinimize;
            application.DisplayAlerts = MSWord.WdAlertLevel.wdAlertsNone;
            document = application.Documents.Add();
            //document.PageSetup.Orientation = MSWord.WdOrientation.wdOrientLandscape;
            document.PageSetup.LeftMargin = 20;
            document.PageSetup.RightMargin = 20;
            document.PageSetup.TopMargin = 20;
            document.PageSetup.BottomMargin = 20;
            document.PageSetup.PageWidth = pagewidth + 40;

            MSWord.Paragraph prg = document.Paragraphs.Add();
            prg.Range.Text = "Query results for " + qname + " [" + DateTime.Now + "]";
            prg.Range.set_Style(MSWord.WdBuiltinStyle.wdStyleTitle);
            //prg.Range.ParagraphFormat.Alignment = MSWord.WdParagraphAlignment.wdAlignParagraphCenter;
            prg.SpaceAfter = 100;
            prg.SpaceBefore = 100;
            prg.Range.InsertParagraphAfter();

            prg = document.Paragraphs.Add();
            prg.Range.Text = "Table of Contents";
            prg.Range.set_Style(MSWord.WdBuiltinStyle.wdStyleTocHeading);
            prg.Range.InsertParagraphAfter();

            prg = document.Paragraphs.Add();
            prg.Range.Text = "TOC";
            prg.Range.InsertParagraphAfter();
            prg.Range.InsertBreak(MSWord.WdBreakType.wdPageBreak);

            prg = document.Paragraphs.Add();
            MSWord.Table qtable = document.Tables.Add(prg.Range, qdata.Length, 1);
            prg.Range.InsertParagraphAfter();

            prg = document.Paragraphs.Add();
            prg.Range.Text = "Appendix";
            prg.Range.set_Style(MSWord.WdBuiltinStyle.wdStyleTitle);
            prg.Range.InsertParagraphAfter();

            object styleTypeTable = MSWord.WdStyleType.wdStyleTypeTable;
            MSWord.Style styl = document.Styles.Add("New Table Style", ref styleTypeTable);
            styl.ParagraphFormat.LineSpacingRule = MSWord.WdLineSpacing.wdLineSpaceSingle;
            styl.ParagraphFormat.SpaceAfter = 0;
            styl.ParagraphFormat.SpaceBefore = 0;
            styl.Table.TopPadding = 0;
            styl.Table.BottomPadding = 0;
            styl.Table.LeftPadding = 0;
            styl.Table.RightPadding = 0;
            //styl.Table.Borders.Enable = 1;
            qtable.Range.set_Style(styl);

            MSWord.Cell cell = qtable.Cell(1, 1);

            int headerwidth = 85;
            int levelwidth = 100;

            object rows, cols;           
            for (int i = 0; i < qdata.Length; i++)
            {   
                int level = int.Parse(qdata[i][1]);
                if(level > 0) 
                {
                    rows = 1;
                    cols = 2;
                    cell.Split(ref rows, ref cols);
                    cell.Range.Cells.SetWidth(level * levelwidth, MSWord.WdRulerStyle.wdAdjustSameWidth);
                    cell = cell.Next;
                }

                rows = 1 + (string.IsNullOrWhiteSpace(qdata[i][0]) ? 0 : 1) +(string.IsNullOrWhiteSpace(qdata[i][6]) ? 0 : 1);
                cols = 2;
                cell.Split(ref rows, ref cols);
                cell.Merge(cell.Next);

                string title = String.Format("{0} {1} ({2})",
                    qdata[i][2],
                    (qdata[i][5].Length > 128 ? qdata[i][5].Remove(128) : qdata[i][5]).Replace("\n", "").Replace("\r", "").Replace("\t", ""),
                    qdata[i][4]);
               
                cell.Range.Text = title;
                cell.Range.Font.Bold = 1;
                cell.Range.set_Style(hstyles[level<8 ? level:7]); 
                cell = cell.Next;

                dlg.UpdateProgress("Exporting Work Item query to Microsoft Word document", "Adding to Word document " + qdata[i][3] + " #" + qdata[i][4], "status", progress++, 100, false, out bcanceled);
                if (progress == 100) progress = 0;
                if (bcanceled)
                {
                    application.Visible = true;
                    Marshal.ReleaseComObject(document);
                    Marshal.ReleaseComObject(application);

                    return true;
                }

                /*cell.Range.Text = "Title";
                cell.Range.Cells.SetWidth(headerwidth, MSWord.WdRulerStyle.wdAdjustSameWidth);
                cell.Range.Font.Bold = 1;
                cell = cell.Next;

                cell.Range.Text = qdata[i][4];          
                cell = cell.Next;*/

                /*cell.Range.Text = "Description";
                cell.Range.Cells.SetWidth(headerwidth, MSWord.WdRulerStyle.wdAdjustSameWidth);
                cell.Range.Font.Bold = 1;
                cell = cell.Next;*/

                if (!string.IsNullOrWhiteSpace(qdata[i][6]))
                {
                    cell.Merge(cell.Next);
                    cell.Range.Text = qdata[i][6];
                    cell = cell.Next;
                }

                if(!string.IsNullOrWhiteSpace(qdata[i][0]))
                {
                    cell.Range.Text = "Attachments";
                    cell.Range.Cells.SetWidth(headerwidth, MSWord.WdRulerStyle.wdAdjustSameWidth);
                    cell.Range.Font.Bold = 1;
                    cell = cell.Next;

                    var query = qdata[i][0]
                                        .Split(';')
                                        .Where(x => !string.IsNullOrWhiteSpace(x))
                                        .Select(x => 
                                        {
                                            string[] ch = x.Split('~');
                                            return new {name=ch[0], value=ch[1]};
                                        }).ToArray();
                    cell.Split(query.Length, 1);

                    foreach (var kvp in query)
                    {
                        string localpath = Path.GetTempFileName() +"."+ kvp.name;
                        //try { File.Delete(localpath); }
                        //catch { }
                        
                        try
                        {
                            webclient.DownloadFile(kvp.value, localpath);
                        }
                        catch(Exception ex)
                        {
                            localpath = "";
                            OutputCommandString(ex.ToString(), AppTitle);
                        }

                        prg = document.Paragraphs.Add();
                        prg.Range.Text = kvp.name;
                        prg.Range.set_Style(MSWord.WdBuiltinStyle.wdStyleHeading3);

                        cell.Range.Text = kvp.name;
                        document.Hyperlinks.Add(cell.Range, missing, prg.Range);

                        prg.Range.InsertParagraphAfter();
                        document.InlineShapes.AddHorizontalLineStandard(prg.Range);
                        prg = document.Paragraphs.Add();

                        if (!string.IsNullOrEmpty(localpath))
                        {
                            try
                            {
                                Image img = Image.FromFile(localpath);
                                img.Dispose();
                                document.InlineShapes.AddPicture(localpath, false, true, prg.Range);
                            }
                            catch
                            {
                                if (Path.GetExtension(kvp.name).Equals(".sql", StringComparison.InvariantCultureIgnoreCase) ||
                                    Path.GetExtension(kvp.name).Equals(".txt", StringComparison.InvariantCultureIgnoreCase))
                                {
                                    prg.Range.InsertFile(localpath);//, prg.Range, false, true, false);
                                }
                                else
                                {
                                    MSWord.InlineShape shape = document.InlineShapes.AddOLEObject(missing, localpath, false, false, missing, missing, missing, prg.Range);
                                    if (shape.OLEFormat.ClassType.ToString() != "Package")
                                        shape.Width = document.PageSetup.PageWidth - 40;
                                }
                            }
                        }
                        cell = cell.Next;
                    }
                    if (query.Length == 0) cell = cell.Next;
                }
            }

            object styleTypePara = MSWord.WdStyleType.wdStyleTypeParagraph;
            MSWord.Style styl2 = document.Styles.Add("New Paragraph Style", ref styleTypePara);
            //styl2.ParagraphFormat.set_Style(MSWord.WdBuiltinStyle.wdStyleNormal);
            styl2.ParagraphFormat.LeftIndent = 100;
            styl2.ParagraphFormat.RightIndent = 100;
            styl2.ParagraphFormat.LineSpacingRule = MSWord.WdLineSpacing.wdLineSpaceSingle;
            styl2.ParagraphFormat.SpaceAfter = 0;
            styl2.ParagraphFormat.SpaceBefore = 0;

            MSWord.Paragraph tocpara = document.Paragraphs[3];
            MSWord.TableOfContents tblct = document.TablesOfContents.Add(tocpara.Range, missing, 1, 1);    
            tblct.Update();
            //tblct.Range.set_Style(styl2);  

            //application.Visible = true;

            Marshal.ReleaseComObject(document);
            Marshal.ReleaseComObject(application);

            return false;
        }

        private MSWord.Style CreateTableStyle(ref MSWord.Document wdDoc)
        {
            MSWord.WdBorderType verticalBorder = MSWord.WdBorderType.wdBorderVertical;
            MSWord.WdBorderType leftBorder = MSWord.WdBorderType.wdBorderLeft;
            MSWord.WdBorderType rightBorder = MSWord.WdBorderType.wdBorderRight;
            MSWord.WdBorderType topBorder = MSWord.WdBorderType.wdBorderTop;
            
            MSWord.WdLineStyle doubleBorder = MSWord.WdLineStyle.wdLineStyleDouble;
            MSWord.WdLineStyle singleBorder = MSWord.WdLineStyle.wdLineStyleSingle;

            MSWord.WdTextureIndex noTexture = MSWord.WdTextureIndex.wdTextureNone;
            MSWord.WdColor gray10 = MSWord.WdColor.wdColorGray10;
            MSWord.WdColor gray70 = MSWord.WdColor.wdColorGray70;
            MSWord.WdColorIndex white = MSWord.WdColorIndex.wdWhite;

            object styleTypeTable = MSWord.WdStyleType.wdStyleTypeTable;
            MSWord.Style styl = wdDoc.Styles.Add("New Table Style", ref styleTypeTable);

            styl.Font.Name = "Arial";
            styl.Font.Size = 11;
            styl.Table.Borders.Enable = 1;

            MSWord.ConditionalStyle evenRowBanding = styl.Table.Condition(MSWord.WdConditionCode.wdEvenRowBanding);
            evenRowBanding.Shading.Texture = noTexture;
            evenRowBanding.Shading.BackgroundPatternColor = gray10;
            // Borders have to be set specifically for every condition.
            evenRowBanding.Borders[leftBorder].LineStyle = doubleBorder;
            evenRowBanding.Borders[rightBorder].LineStyle = doubleBorder;
            evenRowBanding.Borders[verticalBorder].LineStyle = singleBorder;

            MSWord.ConditionalStyle firstRow = styl.Table.Condition(MSWord.WdConditionCode.wdFirstRow);
            firstRow.Shading.BackgroundPatternColor = gray70;
            firstRow.Borders[leftBorder].LineStyle = doubleBorder;
            firstRow.Borders[topBorder].LineStyle = doubleBorder;
            firstRow.Borders[rightBorder].LineStyle = doubleBorder;
            firstRow.Font.Size = 14;
            firstRow.Font.ColorIndex = white;
            firstRow.Font.Bold = 1;

            // Set the number of rows to include in a "band".
            styl.Table.RowStripe = 1;
            return styl;
        }

        /*private void ExecuteQuery(WorkItemStore wistore, QueryDefinition qdef, string ProjectName, out string[] qheader, out string[][] qdata)
        {
            WorkItemLinkInfo[] links = null;
            Hashtable context = new Hashtable();
            List<Tag> qresults = new List<Tag>();
            StringBuilder strb = new StringBuilder();
            List<string[]> lqdata = new List<string[]>();
            IEnumerable<string> ieheader;

            context.Add("project", ProjectName); //@me, @today are filled automatically
            var query = new Query(wistore, qdef.QueryText, context);

            strb.Clear();
            foreach (FieldDefinition cols in query.DisplayFieldList)
            {
                strb.Append(cols.Name + "\t");
            }
            OutputCommandString(strb.ToString(), AppTitle);

            ieheader = query.DisplayFieldList.Cast<FieldDefinition>().Select(x => x.Name);

            if (query.IsLinkQuery)
            {
                links = query.RunLinkQuery();

                WorkItemLinkTypeEnd[] linkTypes = null;
                if(query.IsTreeQuery) linkTypes = query.GetLinkTypes();
                
                int idx, flag, level = 0;
                foreach (var wilnk in links)
                {
                    if(wilnk.SourceId == 0) qresults.Add(new Tag() {tWorkItemID = wilnk.TargetId, tlevel = 0});                   
                }

                if(qresults.Count > 0)
                {
                    while(true)
                    {
                        level++;
                        flag = 0;
                        foreach (var wilnk in links)
                        {
                            if(wilnk.SourceId == 0) continue;

                            string lnkname = query.IsTreeQuery ? linkTypes.First(x => x.Id == wilnk.LinkTypeId).ImmutableName : wilnk.LinkTypeId.ToString();

                            if (((idx = qresults.IndexOf(new Tag() { tWorkItemID = wilnk.SourceId })) >= 0)
                                    && !qresults.Contains(new Tag() { tWorkItemID = wilnk.TargetId })) 
                            {
                                qresults.Insert(idx + 1, new Tag() { tWorkItemID = wilnk.TargetId, tlevel = level, tLinkTypeID = lnkname });
                                flag = 1;
                            }
                        }
                        if(flag == 0) break;
                    }
                }

                foreach (var tag in qresults)
                {
                    strb.Clear();
                    WorkItem wi = wistore.GetWorkItem(tag.tWorkItemID);

                    strb.Clear();
                    string stratt;
                    foreach (Attachment att in wi.Attachments)
                    {
                        strb.AppendLine(att.Uri.ToString());
                    }
                    stratt = strb.ToString();
                    OutputCommandString(stratt, AppTitle);

                    foreach (FieldDefinition cols in query.DisplayFieldList)
                    {
                        if (cols.Name == "Link Type") strb.Append(tag.tLinkTypeID + "\t");
                        else
                        {
                            try
                            {
                                strb.Append(wi[cols.Name].ToString() + "\t");
                            }
                            catch
                            {
                                strb.Append("\t");
                            }
                        }
                    }
                    OutputCommandString(new String(' ', tag.tlevel) + strb.ToString(), AppTitle);

                    lqdata.Add(
                        (new[] { stratt })
                        .Concat(new[] { tag.tlevel.ToString() })
                        .Concat(query.DisplayFieldList
                            .Cast<FieldDefinition>()
                            .Select(x =>
                            {
                                if (x.Name == "Link Type") return tag.tLinkTypeID;
                                else
                                {
                                    try
                                    {
                                        return wi[x.Name].ToString();
                                    }
                                    catch
                                    {
                                        return string.Empty;
                                    }
                                }
                            }))
                        .ToArray());
                }
                ieheader = (new[] { "Level" }).Concat(ieheader);
            }
            else
            {
                foreach (WorkItem wi in query.RunQuery())
                {
                    strb.Clear();
                    string stratt;
                    foreach (Attachment att in wi.Attachments)
                    {
                        strb.AppendLine(att.Uri.ToString());
                    }
                    stratt = strb.ToString();
                    OutputCommandString(stratt, AppTitle);
                    
                    strb.Clear();
                    foreach (FieldDefinition cols in query.DisplayFieldList)
                    {
                        strb.Append(wi[cols.Name].ToString() + "\t");
                    }
                    OutputCommandString(strb.ToString(), AppTitle);

                    lqdata.Add(
                        (new[] { stratt })
                        .Concat(query.DisplayFieldList
                            .Cast<FieldDefinition>()
                            .Select(x => wi[x.Name].ToString()))
                        .ToArray());
                }
            }

            qdata = lqdata.ToArray();
            qheader = (new[] {"Attachments"}).Concat(ieheader).ToArray();
        }*/

        private bool ExecuteQueryLimitedFields(WorkItemStore wistore, QueryDefinition qdef, string ProjectName, out string[][] qdata)
        {
            bool bcanceled;
            int progress = 1;
            int locidx = 1;
            int maxlevel = 32;
            WorkItemLinkInfo[] links = null;
            Hashtable context = new Hashtable();
            List<Tag> qresults = new List<Tag>();
            StringBuilder strb = new StringBuilder();
            List<string[]> lqdata = new List<string[]>();

            context.Add("project", ProjectName); //@me, @today are filled automatically
            var query = new Query(wistore, qdef.QueryText, context);

            if (query.IsLinkQuery)
            {
                links = query.RunLinkQuery();

                WorkItemLinkTypeEnd[] linkTypes = null;
                if (query.IsTreeQuery)
                {
                    linkTypes = query.GetLinkTypes();
                }
                else
                {
                    maxlevel = 1;
                }

                int idx, flag, level = 0, origidx = 1;
                string origprefix = "";
                foreach (var wilnk in links)
                {
                    if (wilnk.SourceId == 0) 
                        qresults.Add(new Tag() { tWorkItemID = wilnk.TargetId, tlevel = 0, tParentID = 0, tPrefix = (locidx++).ToString() });

                    OutputCommandString(string.Format("SourceId={0} TargetId={1} LinkTypeId={2}", wilnk.SourceId, wilnk.TargetId, wilnk.LinkTypeId), AppTitle);
                }

                List<int> chldarr = new List<int>();
                if (qresults.Count > 0)
                {
                    while (level < maxlevel)
                    {
                        level++;
                        flag = 0;
                        locidx = 1;
                        foreach (var wilnk in links.Where(x => x.SourceId > 0))
                        {
                            string lnkname = query.IsTreeQuery ? linkTypes.First(x => x.Id == wilnk.LinkTypeId).ImmutableName : wilnk.LinkTypeId.ToString();

                            idx = -1;
                            if ((idx = qresults.IndexOf(new Tag() { tWorkItemID = wilnk.SourceId, tlevel = level - 1 })) >= 0)
                            {
                                if (origprefix != qresults[idx].tPrefix)
                                {
                                    if (locidx > 2)
                                    {
                                        var qrescopy = qresults.Skip(origidx + 1).Take(locidx - 1);

                                        OutputCommandString("level="+level+", qrescopy=" + qrescopy.Select(x => x.tPrefix).Aggregate((x, y) => x + "," + y), AppTitle);

                                        var orig = qrescopy.GetEnumerator();
                                        var prefix = qrescopy.Select(x => x.tPrefix).Reverse().GetEnumerator();
                                        while (orig.MoveNext() && prefix.MoveNext()) orig.Current.tPrefix = prefix.Current;
                                    }

                                    locidx = 1;
                                    origprefix = qresults[idx].tPrefix;
                                    origidx = idx;
                                }
                                qresults.Insert(idx + 1, new Tag() { tWorkItemID = wilnk.TargetId, tlevel = level, tLinkTypeID = lnkname, tParentID = wilnk.SourceId, tPrefix = origprefix+"."+(locidx++).ToString() });
                                flag = 1;
                                dlg.UpdateProgress("Exporting Work Item query to Microsoft Word document", "Parsing Work Item #" + wilnk.TargetId.ToString(), "status", progress++, 100, false, out bcanceled);
                                if (progress == 100) progress = 0;
                                if (bcanceled)
                                {
                                    flag = 0;
                                    break;
                                }
                            }
                        }
                        if (flag == 0) break;
                    }
                }

                foreach (var tag in qresults)
                {
                    strb.Clear();
                    WorkItem wi = wistore.GetWorkItem(tag.tWorkItemID);

                    strb.Clear();
                    string stratt;
                    foreach (Attachment att in wi.Attachments)
                    {
                        strb.Append(att.Name + "~" + att.Uri.ToString() + ";");
                    }
                    stratt = strb.ToString();
                    OutputCommandString(stratt, AppTitle);

                    lqdata.Add(new[] { stratt, 
                                        tag.tlevel.ToString(), 
                                        tag.tPrefix,
                                        wi[CoreFieldReferenceNames.WorkItemType].ToString(),
                                        wi[CoreFieldReferenceNames.Id].ToString(),
                                        wi[CoreFieldReferenceNames.Title].ToString(),
                                        wi[CoreFieldReferenceNames.Description].ToString()});

                    dlg.UpdateProgress("Exporting Work Item query to Microsoft Word document", "Adding to collection Work Item #" + wi.Id.ToString(), "status", progress++, 100, false, out bcanceled);
                    if (progress == 100) progress = 0;
                    if (bcanceled)
                    {
                        break;
                    }
                    OutputCommandString(string.Format("ParentID={0}, WorkItemID={1}, Level={2}", tag.tParentID, tag.tWorkItemID, tag.tlevel), AppTitle);
                }
            }
            else
            {
                locidx = 1;
                foreach (WorkItem wi in query.RunQuery())
                {
                    strb.Clear();
                    string stratt;
                    foreach (Attachment att in wi.Attachments)
                    {
                        strb.Append(att.Name + "~" + att.Uri.ToString() + ";");
                    }
                    stratt = strb.ToString();
                    OutputCommandString(stratt, AppTitle);

                    string witype = wi[CoreFieldReferenceNames.WorkItemType].ToString();
                    string wiid = wi[CoreFieldReferenceNames.Id].ToString();
                    lqdata.Add(new[] { stratt, "0",
                                        (locidx++).ToString(),
                                        witype,
                                        wiid,
                                        wi[CoreFieldReferenceNames.Title].ToString(),
                                        wi[CoreFieldReferenceNames.Description].ToString()});

                    dlg.UpdateProgress("Exporting Work Item query to Microsoft Word document", "Parsing " + witype + " #" + wiid, "status", progress++, 100, false, out bcanceled);
                    if (progress == 100) progress = 0;
                    if(bcanceled)
                    {
                        break;
                    }
                }
            }

            qdata = lqdata.ToArray();

            return false;
        }

        /// <summary>
        /// This function is the callback used to execute a command when the a menu item is clicked.
        /// See the Initialize method to see how the menu item is associated to this function using
        /// the OleMenuCommandService service and the MenuCommand class.
        /// </summary>
        private void MenuItemCallback(object sender, EventArgs e)
        {
            var dte = (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE));
            //IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));

            var TFSExt = dte.GetObject("Microsoft.VisualStudio.TeamFoundation.TeamFoundationServerExt") as TeamFoundationServerExt;
            
            var tfscoll = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(TFSExt.ActiveProjectContext.DomainUri));
            tfscoll.Connect(Microsoft.TeamFoundation.Framework.Common.ConnectOptions.None);
            tfscoll.EnsureAuthenticated();

            var vcsrv = (VersionControlServer)tfscoll.GetService(typeof(VersionControlServer));

            string AppTitle = Resources.ResourceManager.GetString("AppTitle");

            DocumentService docsrv = (DocumentService)GetService(typeof(DocumentService));
            //WorkItemStore wistore = (WorkItemStore)tfscoll.GetService(typeof(WorkItemStore));

            object _lockToken1 = new object();
            object _lockToken2 = new object();

            IWorkItemTrackingDocument doc = docsrv.FindDocument(dte.ActiveDocument.FullName, _lockToken2);
            if (doc == null) return;

            int fldid;
            bool isSortable;
            var dataprov = (doc as IResultsDocument).ResultListDataProvider;
            dataprov.GetFieldInfo("field name", out fldid, out isSortable);

            var qdoc = (doc as IResultsDocument).QueryDocument;// as QueryDocument;      
            /*
Microsoft.VisualStudio.TeamFoundation.WorkItemTracking.PackageHelper.OpenQueryInOffice()
Microsoft.VisualStudio.TeamFoundation.WorkItemTracking.QueryDocument

Microsoft.TeamFoundation.OfficeIntegration.Client.DocumentLaunch
Microsoft.TeamFoundation.OfficeIntegration.Client.ExcelAddIn.CreateDocumentInternal()
Microsoft.TeamFoundation.OfficeIntegration.Client.ELeadWorkbook.CreateList()
             * /

            foreach (int i in (doc as IResultsDocument).SelectedItemIds)
            {
                string val = dataprov.GetFieldValue(dataprov.GetItemIndex(i), fldid);
                /*IWorkItemDocument widoc = docsrv.GetWorkItem(tfscoll, i, _lockToken1);
                if (widoc == null) continue;


                widoc.Load();

                wicoll.Add(widoc.Item.Id);

                widoc.Release(_lockToken1);
            }*/
            doc.Release(_lockToken2);

            //wistore.DestroyWorkItems(wicoll);
        }

        private void OutputCommandString(string text, string caption)
        {
            // --- Build the string to write on the debugger and Output window. 
            var outputText = new StringBuilder();
            outputText.AppendFormat("{0}: {1} ", caption, text);
            outputText.AppendLine();
            // --- Get a reference to IVsOutputWindow. 
            var outputWindow = GetService(typeof(SVsOutputWindow)) as IVsOutputWindow;
            if (outputWindow == null) return;

            // --- Get the window pane for the general output. 
            var guidGeneral = Microsoft.VisualStudio.VSConstants.GUID_OutWindowDebugPane;
            IVsOutputWindowPane windowPane;

            if (Microsoft.VisualStudio.ErrorHandler.Failed(outputWindow.GetPane(ref guidGeneral, out windowPane)))
            {
                return;
            }
            // --- As the last step, write to the output window pane 
            windowPane.OutputString(outputText.ToString());
            windowPane.Activate();
        }

    }
}
